package com.yunquan.ohana.ui.calendar.event.search

import android.app.Application
import android.os.Handler
import android.os.Looper
import androidx.databinding.ObservableArrayList
import androidx.databinding.ObservableField
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
import com.yunquan.ohana.BR
import com.yunquan.ohana.R
import com.yunquan.ohana.base.binding.command.BindingAction
import com.yunquan.ohana.base.binding.command.BindingCommand
import com.yunquan.ohana.base.bus.RxBus
import com.yunquan.ohana.base.ui.AppManager
import com.yunquan.ohana.base.ui.BaseViewModel
import com.yunquan.ohana.db.DBFrames
import com.yunquan.ohana.dialog.LoadingDialog
import com.yunquan.ohana.entity.EventEntity
import com.yunquan.ohana.entity.EventType
import com.yunquan.ohana.entity.FramesDataManager
import com.yunquan.ohana.entity.FramesEntity
import com.yunquan.ohana.entity.LocalFrameEntity
import com.yunquan.ohana.entity.ScheduleModel
import com.yunquan.ohana.entity.SchedulesVo
import com.yunquan.ohana.http.BaseHttp
import com.yunquan.ohana.http.exception.ApiException
import com.yunquan.ohana.http.observer.BaseObserver
import com.yunquan.ohana.http.transformer.CommonTransformer
import com.yunquan.ohana.ui.calendar.event.dialog.EventSyncDialog
import com.yunquan.ohana.ui.calendar.event.week.dialog.BottomDeleteDialog
import com.yunquan.ohana.utils.DateUtils
import com.yunquan.ohana.utils.StringUtils
import com.yunquan.ohana.utils.ToastUtils
import com.yunquan.ohana.utils.Utils
import io.reactivex.rxjava3.disposables.Disposable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.tatarka.bindingcollectionadapter2.ItemBinding
import okhttp3.RequestBody.Companion.toRequestBody
import java.time.DayOfWeek
import java.time.Duration
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import java.time.temporal.TemporalAdjusters
import java.util.Locale
import kotlin.collections.set

class EventSearchViewModel(application: Application) : BaseViewModel(application) {

    val items = ObservableArrayList<EventSearchItemViewModel>()
    val itemBinding = ItemBinding.of<EventSearchItemViewModel>(
        BR.eventSearchItemViewModel,
        R.layout.item_search_event,
    )

    val searchText = ObservableField<String>()
    val isEdit = ObservableField(false)

    var mDeviceId: String? = null
    var mStartDateTime: LocalDateTime? = null
    var mEndDateTime: LocalDateTime? = null

    val onEditClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            val b = isEdit.get()!!
            isEdit.set(!b)
            items.forEach { it.checked.set(isEdit.get()) }
        }
    })
    val onClearClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            searchText.set(null)
        }
    })

    val onDeleteClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            val list = items.filter { it.isSelect.get() == true }.map { it.data!! }
            if (list.isNotEmpty()) {
                val deleteDialog = BottomDeleteDialog(AppManager.appManager.currentActivity()!!)
                deleteDialog.setTitle(Utils.instance.getString(R.string.delete))
                deleteDialog.setTip(Utils.instance.getString(R.string.task_delete_tip))
                deleteDialog.setOnDeleteClick { dialog ->
                    delete(list)
                }
                deleteDialog.show()
            } else {
                ToastUtils.showShort(R.string.please_select_the_schedule_you_want_to_delete)
            }
        }
    })

    override fun onCreate(owner: LifecycleOwner) {
        super.onCreate(owner)
        val (start, end) = DateUtils.getMonthRange(LocalDateTime.now())
        mStartDateTime = start
        mEndDateTime = end
    }

    fun delete(data: List<ScheduleModel>) {
        val map = mutableMapOf<String, Any?>()
        map["deviceId"] = mDeviceId
        map["scheduleId"] = data.map { it.id }
        val body = Gson().toJson(map).toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.deleteSchedule(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<Boolean>() {
                override fun onSuccess(result: Boolean?) {
                    super.onSuccess(result)
                    if (result == true) {
                        search()
                    }
                }
            })
    }

    fun search() {
        if (StringUtils.isEmpty(searchText.get())) {
            items.clear()
            return
        }
        val params = SchedulesVo(
            deviceId = mDeviceId,
            title = searchText.get(),
            startTime = mStartDateTime!!.atZone(ZoneId.systemDefault()).toEpochSecond().toString(),
            endTime = mEndDateTime!!.atZone(ZoneId.systemDefault()).toEpochSecond().toString(),
        )
        val body = Gson().toJson(params).toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.getAllSchedulesByDeviceId(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<List<ScheduleModel>>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    showLoading()
                }

                override fun onSuccess(result: List<ScheduleModel>?) {
                    super.onSuccess(result)
                    if (result != null) {
                        setShowData(result)
                    }
                }

                override fun onComplete() {
                    super.onComplete()
                    hideLoading()
                }
            })
    }

    fun setShowData(data: List<ScheduleModel>) {
        viewModelScope.launch(Dispatchers.IO) {
            val showMap = mutableMapOf<String, MutableList<ScheduleModel>>()
            for (e in data) {
                setEveryDayEvent(e, showMap)
            }
            withContext(Dispatchers.Main) {
                hideLoading()
                items.clear()
                showMap.forEach { (key, value) ->
                    value.forEach {
                        items.add(
                            EventSearchItemViewModel(
                                this@EventSearchViewModel,
                                key,
                                it,
                                isEdit.get()!!
                            )
                        )
                    }
                }
            }
        }
    }

    private fun setEveryDayEvent(
        e: ScheduleModel,
        showMap: MutableMap<String, MutableList<ScheduleModel>>
    ) {
        val zone = ZoneId.systemDefault()
        val startLdt = LocalDateTime.ofInstant(Instant.ofEpochSecond(e.startTime!!.toLong()), zone)
        val endLdt = LocalDateTime.ofInstant(Instant.ofEpochSecond(e.endTime!!.toLong()), zone)

        val dayFormatter =
            DateTimeFormatter.ofPattern(DateUtils.FORMAT.DATE_HORIZONTAL, Locale.ENGLISH)
        val startDay = startLdt.toLocalDate().format(dayFormatter)
        val endDay = endLdt.toLocalDate().format(dayFormatter)

        e.startDate = startDay
        e.endDate = endDay
        if (startDay == endDay) {
            val timeFormatter12 = DateTimeFormatter.ofPattern("hh:mm a", Locale.ENGLISH)
            e.startTimeStr = startLdt.format(timeFormatter12)
            e.endTimeStr = endLdt.format(timeFormatter12)
            showMap.getOrPut(startDay) { mutableListOf() }.add(e)
        } else {
            val dayDiff = ChronoUnit.DAYS.between(startLdt.toLocalDate(), endLdt.toLocalDate()) + 1
            val timeFormatter24 = DateTimeFormatter.ofPattern("HH:mm a", Locale.ENGLISH)
            for (i in 0 until dayDiff.toInt()) {
                val bean = e.copy()
                val dayDate = startLdt.toLocalDate().plusDays(i.toLong())
                val day = dayDate.format(dayFormatter)

                when (day) {
                    startDay -> {
                        // 第一天，开始时间到23:59:59
                        bean.startTimeStr = startLdt.format(timeFormatter24)
                        bean.endTimeStr = "23:59 PM"
                    }

                    endDay -> {
                        // 最后一天，00:00:00到结束时间
                        bean.startTimeStr = "00:00 AM"
                        bean.endTimeStr = endLdt.format(timeFormatter24)
                    }

                    else -> {
                        // 中间天，00:00:00到23:59:59
                        bean.startTimeStr = "00:00 AM"
                        bean.endTimeStr = "23:59 PM"
                    }
                }
                showMap.getOrPut(day) { mutableListOf() }.add(bean)
            }
        }
    }

    fun bindFrames(code: String) {
        val map = mutableMapOf<String, Any>()
        map["qrcode"] = code
        val json = Gson().toJson(map)
        val body = json.toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.bindDevice(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<Boolean>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    LoadingDialog.getInstance().show()
                }

                override fun onSuccess(result: Boolean?) {
                    super.onSuccess(result)
                    if (result == true) {
                        getFrames()
                    } else {
                        ToastUtils.showShort(R.string.bind_failed)
                        LoadingDialog.getInstance().dismiss()
                    }
                }

                override fun onError(e: ApiException) {
                    super.onError(e)
                    LoadingDialog.getInstance().dismiss()
                }
            })
    }

    fun getFrames() {
        val map = mutableMapOf<String, Any>()
        val json = Gson().toJson(map)
        val body = json.toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.getUserDeviceList(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<List<FramesEntity>>() {
                override fun onSuccess(result: List<FramesEntity>?) {
                    super.onSuccess(result)
                    if (result?.isNotEmpty() == true) {
                        FramesDataManager.instance.setData(result)
                        for (i in result.indices) {
                            val e = result[i]
                            val bean = LocalFrameEntity(
                                deviceId = e.id,
                                name = e.name,
                                location = e.roomLocation,
                                online = if (e.online!!) 1 else 0
                            )
                            DBFrames.instance.saveOrUpdate(bean)
                        }
                        Handler(Looper.getMainLooper()).postDelayed({
                            syncToDevice(result)
                        }, 500)
                        RxBus.getDefault().post(EventEntity(EventType.FRESH_DEVICE_LIST))
                    } else {
                        LoadingDialog.getInstance().dismiss()
                    }
                }

                override fun onError(e: ApiException) {
                    super.onError(e)
                    LoadingDialog.getInstance().dismiss()
                }

                override fun onComplete() {
                    super.onComplete()
                    RxBus.getDefault().post(EventEntity(EventType.UPDATE_DEVICE_LIST_COMPLETE))
                }
            })
    }

    fun syncToDevice(devices: List<FramesEntity>) {
        val syncDeviceList = devices.filter { it.id != mDeviceId }
        if(syncDeviceList.isEmpty()){
            return
        }
        val scheduleIds = items.filter { it.isSelect.get() == true }
        if(scheduleIds.isEmpty()){
            return
        }
        val map = mutableMapOf<String, Any>()
        map["scheduleId"] = scheduleIds.map { it.data!!.id!! }
        map["deviceId"] = devices.filter { it.id != mDeviceId }.map { it.id }
        val body = Gson().toJson(map).toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.addSScheduleDeviceRelation(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<Boolean>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    if (!LoadingDialog.getInstance().isShowing()) {
                        LoadingDialog.getInstance().show()
                    }
                }

                override fun onSuccess(result: Boolean?) {
                    super.onSuccess(result)
                    if (result == true) {
                        val b = isEdit.get()!!
                        isEdit.set(!b)
                        items.forEach { it.checked.set(isEdit.get()) }
                        ToastUtils.showShort(R.string.synchronization_is_successful)
                    } else {
                        ToastUtils.showShort(R.string.synchronization_failed)
                    }
                }

                override fun onComplete() {
                    super.onComplete()
                    LoadingDialog.getInstance().dismiss()
                }
            })
    }
}